open Syntaxtree;;

(** Contains the functions to print the Abstract Syntax Tree *)

(** Prints an ide 
    @param Ide(s) The ide to print
*)
let print_ide (Ide(s)) = "Ide("^s^")"

(** Prints a list of string 
    @param al The string list to print
*)
let rec print_string_list (al: string list) = match al with
      []    ->  ""
    | x::xs ->  x ^ "\t" ^ (print_string_list xs)

(** Prints arithmetical expressions 
    @param a The aexp to print
*)
let rec print_aexp  (a: aexp) = match a with
	  N(n)              ->  "N("^ (string_of_int n) ^")"
    | R(f)              ->  "R("^ (string_of_float f) ^")"
	| Var(id)           ->  "Var("^ (print_ide id) ^")"
    | Vec(id,ex)        ->  "Vec("^ (print_ide id) ^","^ (print_aexp ex) ^")"
    | Mat(id,ex1,ex2)   ->  "Mat("^ (print_ide id) ^","^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
	| Sum(ex1,ex2)      ->  "Sum("^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
    | Sub(ex1,ex2)      ->  "Sub("^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
	| Mul(ex1,ex2)      ->  "Mul("^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
	| Div(ex1,ex2)      ->  "Div("^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
    | FCall(id,exl)     ->  "FCall("^ (print_ide id) ^",["^ (print_string_list(List.map print_aexp exl)) ^"])"
    | VecSearch(id,ex)  ->  "\nVecSearc("^ (print_ide id) ^","^ (print_aexp ex) ^")"

(** Prints boolean expressions 
    @param b The bexp to print
*)
and print_bexp (b: bexp) = match b with
	  B(b)              ->  if(b) then "B(true) " else "B(false)"
	| Equ(ex1,ex2)      ->  "Equ(" ^ (print_aexp ex1)^ "," ^(print_aexp ex2)^")"
        | NotEqu(ex1,ex2)      ->  "Equ(" ^ (print_aexp ex1)^ "," ^(print_aexp ex2)^")"
	| LE(ex1,ex2)       ->  "LE(" ^ (print_aexp ex1)^ "," ^(print_aexp ex2)^")"
	| LT(ex1,ex2)       ->  "LT(" ^ (print_aexp ex1)^ "," ^(print_aexp ex2)^")"
	| Not(bex)          ->  "Not(" ^ (print_bexp bex) ^ ")"
	| And(bex1,bex2)    ->  "And(" ^ (print_bexp bex1) ^ "," ^ (print_bexp bex2) ^ ")"
	| Or(bex1,bex2)     ->  "Or(" ^ (print_bexp bex1) ^ "," ^ (print_bexp bex2) ^ ")"

(** Prints left expressions 
    @param l The lexp to print
*)
let print_lexp (l: lexp) = match l with
      LVar(id)          ->  "LVar("^ (print_ide id) ^")"
    | LMat(id,ex1,ex2)  ->  "LMat("^ (print_ide id) ^","^ (print_aexp ex1) ^","^ (print_aexp ex2) ^")"
| LVecModify(id,ex)    ->  "LVecModidy("^ (print_ide id) ^","^ (print_aexp ex) ^")"
    | LVecAdd(id,ex)       ->  "LVecAdd("^ (print_ide id) ^","^ (print_aexp ex) ^")"

(** Prints commands 
    @param c The command to print
*)
let rec print_cmd (c: cmd) = match c with
	  Ass(l,r)          ->  "\tAss(" ^ (print_lexp l) ^ "," ^ (print_aexp r) ^ ")\n"
	| Blk(clist)        ->  "Blk(\n\t" ^ (print_string_list (List.map print_cmd clist)) ^ ")\n"
	| Ite(b,c1,c2)      ->  "\tIte(" ^ (print_bexp b) ^ "," ^ (print_cmd c1) ^ "," ^ (print_cmd c2) ^ ")\n"
	| While(b,c)        ->  "\tWhile(" ^ (print_bexp b) ^ "," ^ (print_cmd c) ^ ")\n"
	| Write(ex)         ->  "\tWrite(" ^ (print_aexp ex) ^ ")\n"
    | PCall(id,exl)     ->  "\tPCall("^ (print_ide id) ^",["^ (print_string_list (List.map print_aexp exl)) ^"])\n"
| VecDelete(id,ex)  ->  "\nVecDelete("^ (print_ide id) ^","^ (print_aexp ex) ^")"
	

(** Prints basic Type
    @param b The basic Type to print
*)
let print_bType (b: bType) = match b with 
      Int    ->  "Int"
    | Float  ->  "Float"

(** Prints generic Type
    @param g The generic Type to print
*)
let print_gType (g: gType) = match g with 
      Basic(b)      ->  "Basic(" ^ (print_bType b) ^ ")"
    | Vector(n,b)   ->  "Vector(" ^ (string_of_int n) ^ "," ^ (print_bType b) ^ ")"
    | Matrix(n,m,b) ->  "Matrix(" ^ (string_of_int n) ^ "," ^ (string_of_int n) ^ "," ^ (print_bType b) ^ ")"

(** Prints a declaration
    @param Dec(id,gt) The declaration to print
*)
let print_dec (Dec(id,gt): dec) = "Dec(" ^ (print_ide id) ^ "," ^ (print_gType gt) ^ ")\n"

(** Prints Parameters of functions/procedures
    @param Par(id,bt) The param to print
*)
let print_param (Par(id, bt): param) = "Par(" ^ (print_ide id) ^ "," ^ (print_bType bt) ^ ")\n"

(** Prints the return value of functions
    @param Ret(b) The return value to print
*)
let print_ret (Ret(b)) = "Ret(" ^ (print_bType b) ^ ")"

(** Prints The definition of procedures and functions
    @param s The procedure/function to print
*)
let print_proc (s: proc) = match s with
            Proc(id,plist,dlist,c)      ->  "\rProc(" ^ (print_ide id) ^ ",\n\t" ^ 
                                                      (print_string_list (List.map print_param plist)) ^ 
                                                      (print_string_list (List.map print_dec dlist)) ^ "\n\t" ^ 
                                                      (print_cmd c) ^ ")\n\n"
          | Func(id,plist,r,dlist,c)    ->  "\rFunc(" ^ (print_ide id) ^ ", " ^ 
                                                      (print_ret r) ^ "\t" ^
                                                      (print_string_list (List.map print_param plist)) ^ "\n\t" ^ 
                                                      (print_string_list (List.map print_dec dlist)) ^ "\n\t" ^ 
                                                      (print_cmd c) ^ ")\n\n"

(** Prints the Abstract Syntax Tree
    @param p The AST
    @param out The output channel
*)
let print_program (p: program) (out: out_channel) = match p with
        Program(dlist,plist,c) ->  output_string out
                                    ("Program(\n\t" ^ (print_string_list (List.map print_dec dlist)) ^ "\n" ^ 
                                                    (print_string_list (List.map print_proc plist)) ^ "\n" ^ 
                                                    (print_cmd c) ^ "\n)")
      | SynError                ->  output_string out "SynError"
